`timescale 1ns/100ps

`include "sim_glb.sv"

module tc;

localparam          CLK_PRD                 = 5;
localparam          DEPTH                   = 3;
localparam          DATA_BW                 = 16;
localparam          RDPL_NUM                = 4;        // Read Data Pipe Line number, MUST >=1
localparam          ADDR_BW                 = $clog2(DEPTH);

reg                                         rst_n;
reg                                         clk;

reg                                         prd_pls;    // periodical pulse with longer period for patroling

// orignal spram read/write signals
reg                                         org_wen;
reg                                         org_ren;
reg                 [ADDR_BW-1:0]           org_addr;
reg                 [DATA_BW-1:0]           org_wdat;
wire                                        org_rvld;
wire                [DATA_BW-1:0]           org_rdat;

// mux patrol, fix and orignal signals
wire                                        mux_wen;
wire                                        mux_ren;
wire                [ADDR_BW-1:0]           mux_addr;
wire                [DATA_BW-1:0]           mux_wdat;
wire                                        mux_rvld;
wire                [DATA_BW-1:0]           mux_rdat;
wire                                        mux_rfix;   // detect error and need to be fixed
wire                                        necc_rvld;
wire                [DATA_BW-1:0]           necc_rdat;

reg                                         cfg_ptrl_ena;
reg                                         cfg_fix_ena;
wire                                        sts_ptrl_inc;
wire                                        sts_fix_inc;
wire                [ADDR_BW-1:0]           sts_fix_addr;

reg                                         cfg_frc_sbe;
wire                                        alm_ecc_err;
wire                                        alm_ecc_dbe;

initial begin:CRG
    rst_n=1'b0;
    clk=1'b0;
    prd_pls=1'b0;

    fork
        rst_n=#100.5 1'b1;
        forever clk=#CLK_PRD ~clk;
        forever begin
            repeat(200) begin
                @(posedge clk);
            end
            prd_pls=`U_DLY 1'b1;
            @(posedge clk);
            prd_pls=`U_DLY 1'b0;
        end
    join
end

RGRS_MNG    rgrs;
initial begin:REGRESS
    rgrs = new("tc_sef_sp", 1);

    rgrs.wait_chks_done(100_000_000);
end

initial begin:GEN_RW
    integer addr;
    reg [31:0]  rand_dat;

    org_wen  <=`U_DLY 1'b0;
    org_ren  <=`U_DLY 1'b0;
    org_addr <=`U_DLY 0;
    org_wdat <=`U_DLY 0;

    cfg_ptrl_ena <=`U_DLY 1'b1;
    cfg_fix_ena  <=`U_DLY 1'b1;

    @(posedge rst_n);
    @(posedge clk);
    for(addr=0; addr<DEPTH; addr=addr+1) begin
        mem_wr_t(0, addr, $urandom());
    end

    repeat(50000) begin
        rand_dat = $urandom();
        addr = $urandom_range(0, DEPTH-1);
        if (rand_dat[0]==1'b1) begin
            mem_rd_t(rand_dat[31-:2], addr);
        end else begin
            mem_wr_t(rand_dat[31-:1], addr, rand_dat[1+:DATA_BW]);
        end
    end
    rgrs.one_chk_done("GEN_RW is done.");
end

initial begin:FRC_ERR
    integer t;
    cfg_frc_sbe <=`U_DLY 1'b0;
    
    @(posedge rst_n);
    #10_000;

    forever begin
        @(posedge clk);
        t = $urandom_range(0, 20);
        if (t>0) begin
            cfg_frc_sbe <=`U_DLY 1'b0;

            repeat(t) begin
                @(posedge clk);
            end
        end
        cfg_frc_sbe <=`U_DLY 1'b1;
    end
end

initial begin:CHK_RDAT
    integer         i;

    @(posedge rst_n);

    forever begin
        @(posedge clk);
        if (necc_rvld==1'b1 || org_rvld==1'b1) begin
            if (necc_rvld==1'b0 || org_rvld==1'b0 || org_rdat!=necc_rdat) begin
                $error("[ERROR] ram read data error");
            end
        end
    end
end

sef_sp #(
        .DEPTH                          (DEPTH                          ),
        .DATA_BW                        (DATA_BW                        ),
        .RDPL_NUM                       (RDPL_NUM                       ) 	// Read Data Pipe Line number, MUST >=1
) u_apf_sp ( 
        .rst_n                          (rst_n                          ),
        .clk                            (clk                            ),

        .prd_pls                        (prd_pls                        ),	// periodical pulse with longer period for patroling

// orignal spram read/write signals
        .org_wen                        (org_wen                        ),
        .org_ren                        (org_ren                        ),
        .org_addr                       (org_addr                       ),
        .org_wdat                       (org_wdat                       ),
        .org_rvld                       (org_rvld                       ),
        .org_rdat                       (org_rdat                       ),

// mux patrol, fix and orignal signals
        .mux_wen                        (mux_wen                        ),
        .mux_ren                        (mux_ren                        ),
        .mux_addr                       (mux_addr                       ),
        .mux_wdat                       (mux_wdat                       ),
        .mux_rdat                       (mux_rdat                       ),
        .mux_rfix                       (mux_rfix                       ),	// detect error and need to be fixed

        .cfg_ptrl_ena                   (cfg_ptrl_ena                   ),
        .cfg_fix_ena                    (cfg_fix_ena                    ),
        .cfg_mem_ena                    (1'b1                           ),
        .sts_ptrl_inc                   (sts_ptrl_inc                   ),
        .sts_fix_inc                    (sts_fix_inc                    ),
        .sts_fix_addr                   (sts_fix_addr                   )
);

assign mux_rfix = alm_ecc_err & (~alm_ecc_dbe);
spram_wrap #(
        .DEPTH                          (DEPTH                          ),
        .DATA_BW                        (DATA_BW                        ),
        .RDPL_NUM                       (RDPL_NUM                       ),	// Read Data Pipe Line number, MUST >=1
        .USER_DEF_TAG                   ("NONE"                         ),
        .ECC_MODE                       ("SEC_DED"                      ) 	// Single Error-bit Correction and Double Error-bit Detecttion
) u_ecc_ram ( 
        .rst_n                          (rst_n                          ),
        .clk                            (clk                            ),

        .mem_wen                        (mux_wen                        ),
        .mem_ren                        (mux_ren                        ),
        .mem_rce                        ({RDPL_NUM{1'b1}}               ),
        .mem_addr                       (mux_addr                       ),
        .mem_wdat                       (mux_wdat                       ),
        .mem_rvld                       (mux_rvld                       ),
        .mem_rdat                       (mux_rdat                       ),

        .cfg_frc_sbe                    (cfg_frc_sbe                    ),
        .alm_ecc_err                    (alm_ecc_err                    ),
        .alm_ecc_dbe                    (alm_ecc_dbe                    )
);

spram_wrap #(
        .DEPTH                          (DEPTH                          ),
        .DATA_BW                        (DATA_BW                        ),
        .RDPL_NUM                       (RDPL_NUM                       ),	// Read Data Pipe Line number, MUST >=1
        .USER_DEF_TAG                   ("NONE"                         ),
        .ECC_MODE                       ("NONE"                         ) 	// no ECC
) u_necc_ram ( 
        .rst_n                          (rst_n                          ),
        .clk                            (clk                            ),

        .mem_wen                        (org_wen                        ),
        .mem_ren                        (org_ren                        ),
        .mem_rce                        ({RDPL_NUM{1'b1}}               ),
        .mem_addr                       (org_addr                       ),
        .mem_wdat                       (org_wdat                       ),
        .mem_rvld                       (necc_rvld                      ),
        .mem_rdat                       (necc_rdat                      ),

        .cfg_frc_sbe                    (1'b0                           ),
        .alm_ecc_err                    (                               ),
        .alm_ecc_dbe                    (                               )
);

task mem_wr_t; 
    input integer   gap;
    input integer   addr;
    input integer   data;
    begin
        repeat(gap) begin
            @(posedge clk);
        end
        org_wen  <=`U_DLY 1'b1;
        org_ren  <=`U_DLY 1'b0;
        org_addr <=`U_DLY addr;
        org_wdat <=`U_DLY data;

        @(posedge clk);
        org_wen  <=`U_DLY 1'b0;
    end
endtask

task mem_rd_t; 
    input integer   gap;
    input integer   addr;
    begin
        repeat(gap) begin
            @(posedge clk);
        end
        org_wen  <=`U_DLY 1'b0;
        org_ren  <=`U_DLY 1'b1;
        org_addr <=`U_DLY addr;

        @(posedge clk);
        org_ren  <=`U_DLY 1'b0;
    end
endtask

endmodule

